/* 
 *  Copyright 2012 CodeMagi, Inc.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.codemagi.servlets;

import com.codemagi.database.*;
import com.codemagi.util.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.commons.collections.map.ReferenceMap;
import org.apache.log4j.*;

/**
 *  QueryCache acts as a cache for SQL query results.
 *
 *  @version 1.0
 *  @author August Detlefsen for CodeMagi, Inc.
 */
public class QueryCache extends HttpServlet {

    //set to true to view debug output         
    Logger log = Logger.getLogger(this.getClass());

    //DB CONNECT INFO  
    protected String jndiConnect = "";

    //MEMBERS
    protected ReferenceMap queries = new ReferenceMap();
    protected ServletContext application;

    protected long defaultCacheTimeout = -1;
    protected ReferenceMap queryTimes = new ReferenceMap();


    /**                                                    
     * Initializes the servlet                                            
     */
    public void init(ServletConfig config) throws ServletException {
        log.debug("com.codemagi.servlets.QueryCache: Initializing");

        //initialize superclass                                               
        super.init(config);

	jndiConnect = config.getInitParameter("jndi.database");
	log.debug("JNDI Connect: " + jndiConnect);
	if (Utils.isEmpty(jndiConnect)) 
	    throw new ServletException("Unable to initialize Lists servlet -No JNDI connect info specified");
	
	//put a reference to this servlet in the application context  
	String name = config.getInitParameter("app.name");
	if ( Utils.isEmpty(name) ) name = "lists";
	log.debug("Placing Lists in context as: " + name);
        application = this.getServletContext();
        application.setAttribute(name, this);
	
	String sCacheTimeout = config.getInitParameter("cacheTimeout");
	if ( !Utils.isEmpty(sCacheTimeout) ) {
	    try {
		defaultCacheTimeout = Long.decode(sCacheTimeout);
		log.debug("Cache Timeout set to: " + defaultCacheTimeout + " msec");
	    } catch (NumberFormatException nfe) {
		log.warn("WARNING: Invalid number format supplied for cacheTimeout parameter");
	    }
	}
    }


    /**                                                              
     * Generates the DataBean to be used for queries. Depends on the application 
     * init parameter "database" for JNDI connect String.
     */
    protected DataSourceBean2 generateDataBean() {
	log.debug("Generating DataSourceBean2 using: " + jndiConnect);
	return new DataSourceBean2( jndiConnect );
    }
    
    
    /**                                                              
     * Generates a GridList from the input query
     *                                                               
     * @param query     DB query to generate results for
     */
    private GridList generateTable(String query) {
	
	DataSourceBean2 dataBean = generateDataBean();

	dataBean.setQuery(query);
	dataBean.executeQuery();
	
	GridList output = dataBean.getResults();
	
	queries.put(query, output);
	queryTimes.put(query, new Date().getTime());
	
	return output;
    }
    
    
    /**                                                              
     * Updates a single query in this cache
     */
    public void update(String query) {
	queries.remove(query);
	queryTimes.remove(query);
    }


    /**                                                              
     * Overridden version of get(String, long) that does not require the cacheTimeout parameter. 
     * This method will use the default cacheTimeout set in the servlet-config.
     */
    public GridList get(String query) {
	return get(query, defaultCacheTimeout);
    }
    
  
    /**                                                              
     * Returns a GridList based on the query selected. Loads the query from the DB if it has not yet been loaded.       
     *                                                               
     * @param query  One of the existing queries defined as fields of this class                         
     * @param defaultElement  Default element to add at the start of the list                          
     */
    public GridList get(String query, long cacheTimeout) {
	
	//get table from hash                                                   
	GridList list = (GridList)queries.get(query);

	try {
	    com.codemagi.util.Timer timer = new com.codemagi.util.Timer(10000);
	    timer.start();

	    //if a cache timeout has been set
	    if (cacheTimeout > -1) {
		//check if the cache has expired
		Long lastUpdate  = (Long)queryTimes.get(query);
		log.debug("QueryCache.get(): cache lastUpdate: " + lastUpdate + " current: " + System.currentTimeMillis() );
		if (lastUpdate == null || (lastUpdate.longValue() + cacheTimeout < System.currentTimeMillis())) {
		    log.debug("QueryCache.get(): cache expired, updating table");
		    list = generateTable(query);
		}
	    } 
	    
	    //if the list was not already in the cache, generate it now
	    if (list == null) {
		log.debug("Lists.get(): query not found, updating table");
		list = generateTable(query);
	    }

	} catch (com.codemagi.util.TimeoutError te) {
	    log.debug("Query timed out", te);
	}
	
	return list;
    }
    
    
    
    

}
